#ifndef __GNUC__
#define min __min
#define max __max
#else 
#define __int64 long long
#endif

#define BitSet bitset<bitsetSize>

#include <iostream>
#include <vector>
#include <string>
#include "mpi.h"
#include "Consts.h"
#include "OperationsCount.h"
#include "Partitions.h"
#include "PrimeNumbers.h"
#include <ctime>
#include <bitset>
using namespace std;

__int64 GetRank(int n); //���������� ����� ��� n
__int64 RankCount(int n, int k, int shift, BitSet factors, 
        bool minLeft, bool maxRight, int index); //����������� ������� ��������

int myid, numprocs;
   
int main(int argc, char *argv[]) {
    int  namelen; //����� ����� ��������
    char processor_name[MPI_MAX_PROCESSOR_NAME]; //��� ��������

	//������������� MPI
    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
    MPI_Comm_rank(MPI_COMM_WORLD,&myid);
    MPI_Get_processor_name(processor_name,&namelen);
          
    if (myid == 0)
     	freopen(outputFileName.c_str(),"w", stdout);

	for (int n = startNumber;n <= maxNumber;n++) {
        double startwtime, endwtime;	//��������� � �������� �����
        if (myid == 0)
			startwtime = MPI_Wtime();

        __int64 res = GetRank(n);
				
        if (myid == 0) { //����� ����������
#ifndef __GNUC__
			printf("N = %d ranks =  %I64d\n", n, res);
#else
			printf("N = %d ranks =  %I64d\n", n, res);
#endif	
			endwtime = MPI_Wtime();
			printf("����� ����������: %.3lf\n\n", endwtime - startwtime);	       
			fflush(stdout);
	    }
	}

    
    MPI_Finalize();
	fclose(stdout);       
	
	fprintf(stderr,"End %d on %s\n", myid, processor_name);
	fflush(stderr);
	return 0;
}

//������� �������� ��� ������� ����������
vector<int> startPartition, endPartition;

__int64 GetRank(int n) {
    MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); //�������� n
	
	__int64 operations = operationsCount.getOperationsCount(n, 1, 0);
	__int64 startOperation = operations / numprocs * myid;
	__int64 endOperation = min(operations, operations / numprocs * (myid + 1));
	
	startPartition = (myid == 0) ? partitions.GetPartition(startOperation, n)
	               : partitions.GetNextPartition(startOperation, n);
    endPartition = partitions.GetPartition(endOperation, n);
          
    BitSet zeroFactors;
	__int64 myres = startPartition.empty() ? 0 : 
            RankCount(n, 1, 0, zeroFactors, true, true, 0);
            
	__int64 res = 0;
	MPI_Reduce(&myres, &res, 1, MPI_LONG_LONG_INT, MPI_SUM, 0, MPI_COMM_WORLD);
	//������������ ���������� � �������� ��� 0-�� ��������
	return res;
}

__int64 RankCount(int n, int k, int shift, BitSet factors, 
        bool minLeft, bool maxRight, int index)
{
    if (n < k) {
        //������ 1, ���� �������� ��������� �� ������� 0 � �� ���������� ���������
        return n == 0 && shift == 0 && !factors.none();
    }
    //���� ��������� � �������� ������ �����������
    if (minLeft) k = max(k, startPartition[index]);
    //���� ��������� � �������� ������� �����������
    if (maxRight && k > endPartition[index]) return 0;
    
    __int64 result = 0;
    
    if (operationsCount.isCanUseK(n, k, shift)) {
        //������ ������������� k
        result += RankCount(n - k, k + 2, 
            operationsCount.getNewShift(k, shift), //����� �����
            factors ^ *primeNumbers.GetDecomposition(k), //������������
            minLeft && k == startPartition[index], //����� ���������
            maxRight && k == endPartition[index], //������
            index + 1); //������� + 1
    }
    
    if (k + 2 <= n) {
       //������� ����� �� ������� index �������� ������� k   
       result += RankCount(n, k + 2, shift, factors, minLeft, maxRight, index);
    }
       
    return result;
}

